/**
 * @file i2s.h
 * @brief i2s driver
 * @date Wed 31 May 2017 07:15:29 PM CST
 * @author liqiang
 *
 * @defgroup I2S I2S
 * @ingroup PERIPHERAL
 * @brief I2S driver
 * @details I2S driver
 *
 * The I2S have three wires: serial data, word select and serial clock and
 * this interface is used for audio data transfer. The HS6621 I2S interface
 * supports both master and slave mode for transmitter and receiver.
 * The I2S supports the standard I2S frame format.
 *
 * @{
 *
 * @example example_audio.c
 * This is an example of how to use the i2s
 *
 */

#ifndef __I2S_H__
#define __I2S_H__

#ifdef __cplusplus
extern "C"
{
#endif

/*********************************************************************
 * INCLUDES
 */
#include "peripheral.h"


/*********************************************************************
 * MACROS
 */
/**
 * i2s rx clock and ws is generated by codec
 */
#define i2s_rx_clock_ws_generated_by_codec()   \
        do { HS_AUDIO->IF_CTRL |= AU_I2S_RX_MS_SEL_MASK; } while (0)
    
/**
 * i2s rx clock and ws is generated by gpio
 */
#define i2s_rx_clock_ws_generated_by_gpio()   \
        do { HS_AUDIO->IF_CTRL &= ~AU_I2S_RX_MS_SEL_MASK; } while (0)
    
/**
 * i2s tx clock and ws is generated by codec
 */
#define i2s_tx_clock_ws_generated_by_codec()   \
        do { HS_AUDIO->IF_CTRL |= AU_I2S_TX_MS_SEL_MASK; } while (0)
    
/**
 * i2s tx clock and ws is generated by gpio
 */
#define i2s_tx_clock_ws_generated_by_gpio()   \
        do { HS_AUDIO->IF_CTRL &= ~AU_I2S_TX_MS_SEL_MASK; } while (0)
    


/*********************************************************************
 * TYPEDEFS
 */
/// I2S work mode
typedef enum
{
    I2S_WORKMODE_SLAVE        = 0,
    I2S_WORKMODE_MASTER       ,
}i2s_workmode_t;

/// I2S PCM mode
typedef enum
{
    I2S_PCMMODE_STEREO        = 0,
    I2S_PCMMODE_MONO          ,
}i2s_pcmmode_t;

/// I2S bit width
typedef enum
{
    I2S_BITWIDTH_IGNORE     = 0,
    I2S_BITWIDTH_12BIT      ,
    I2S_BITWIDTH_16BIT      ,
    I2S_BITWIDTH_20BIT      ,
    I2S_BITWIDTH_24BIT      ,
    I2S_BITWIDTH_32BIT      ,
    I2S_BITWIDTH_64BIT      ,
    I2S_BITWIDTH_128BIT     ,
    I2S_BITWIDTH_192BIT     ,
}i2s_bitwidth_t;

/// I2S sample rate
typedef enum
{
    I2S_SAMPLE_8K           = 8000,
    I2S_SAMPLE_11K          = 11025,
    I2S_SAMPLE_12K          = 12000,
    I2S_SAMPLE_16K          = 16000,
    I2S_SAMPLE_22K          = 22050,
    I2S_SAMPLE_24K          = 24000,
    I2S_SAMPLE_32K          = 32000,
    I2S_SAMPLE_44P1K        = 44100,
    I2S_SAMPLE_48K          = 48000,
    I2S_SAMPLE_96K          = 96000,
}i2s_sample_t;

/// i2s DMA configuration structure
typedef struct
{
    /// Enable fifo
    bool           use_fifo;
    /// DMA fifo buffer
    void          *buffer;
    /// DMA fifo buffer length
    uint32_t       buffer_len;
    /// DMA block linked list item
    dma_llip_t    *block_llip;
    /// DMA block linked list item num
    uint32_t       block_num;
    /// DMA event callback
    dma_callback_t callback;
}i2s_dma_config_t;

/// i2s configuration structure.
typedef struct
{
    /// sample rate
    i2s_sample_t     sample_rate;
    /// sample width
    i2s_bitwidth_t   sample_width;
    /// ws bit width
    i2s_bitwidth_t   ws_width;
    /// work mode
    i2s_workmode_t   workmode;
    /// pcm mode
    i2s_pcmmode_t    pcmmode;
} i2s_config_t;

/*********************************************************************
 * EXTERN VARIABLES
 */


/*********************************************************************
 * EXTERN FUNCTIONS
 */
/**
 * @brief i2s initialize
 *
 * @return None
 **/
void i2s_init(void);

/**
 * @brief i2s config
 *
 * @param[in] config  configuration @ref i2s_config_t
 *
 * @return None
 **/
void i2s_config(const i2s_config_t *config);

/**
 * @brief adc_dma_config()
 *
 * @param[in] dma  NULL: ADC will allocate a new dma; Not NULL: use it as ADC dma
 * @param[in] config  configuration @ref i2s_dma_config_t
 *
 * @retval NULL No DMA valid or error happen
 * @retval NOT_NULL: Current used DMA
 **/
HS_DMA_CH_Type *i2s_dma_config(HS_DMA_CH_Type *dma, const i2s_dma_config_t *config);

/**
 * @brief i2s start
 *
 * @return None
 **/
void i2s_start(void);

/**
 * @brief i2s stop
 *
 * @return None
 **/
void i2s_stop(void);

/**
 * @brief i2s config rx
 *
 * @param[in] config  configuration @ref i2s_config_t
 *
 * @return None
 **/
void i2s_rx_config(const i2s_config_t *config);

/**
 * @brief i2s_dma_config() for rx
 *
 * @param[in] dma  NULL: ADC will allocate a new dma; Not NULL: use it as ADC dma
 * @param[in] config  configuration @ref i2s_dma_config_t
 *
 * @retval NULL No DMA valid or error happen
 * @retval NOT_NULL: Current used DMA
 **/
HS_DMA_CH_Type *i2s_rx_dma_config(HS_DMA_CH_Type *dma, const i2s_dma_config_t *config);

/**
 * @brief i2s start rx
 *
 * @return None
 **/
void i2s_rx_start(const i2s_config_t *config);

/**
 * @brief i2s stop rx
 *
 * @return None
 **/
void i2s_rx_stop(void);

#ifdef __cplusplus
}
#endif

#endif

/** @} */

